home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume18 / mush6.4 / part14 < prev    next >
Encoding:
Internet Message Format  |  1989-03-12  |  31.9 KB

  1. Subject:  v18i036:  Mail user's shell version 6.4, Part14/19
  2. Newsgroups: comp.sources.unix
  3. Sender: sources
  4. Approved: rsalz@uunet.UU.NET
  5.  
  6. Submitted-by: Dan Heller <island!argv@sun.com>
  7. Posting-number: Volume 18, Issue 36
  8. Archive-name: mush6.4/part14
  9.  
  10.  
  11.  
  12. #! /bin/sh
  13. # This is a shell archive.  Remove anything before this line, then unpack
  14. # it by saving it into a file and typing "sh file".  To overwrite existing
  15. # files, type "sh file -c".  You can also feed this as standard input via
  16. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  17. # will see the following message at the end:
  18. #        "End of archive 14 (of 19)."
  19. # Contents:  loop.c
  20. # Wrapped by rsalz@papaya.bbn.com on Mon Mar 13 19:25:20 1989
  21. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  22. if test -f 'loop.c' -a "${1}" != "-c" ; then 
  23.   echo shar: Will not clobber existing file \"'loop.c'\"
  24. else
  25. echo shar: Extracting \"'loop.c'\" \(30008 characters\)
  26. sed "s/^X//" >'loop.c' <<'END_OF_FILE'
  27. X/* loop.c     (c) copyright 1986 (Dan Heller) */
  28. X
  29. X/*
  30. X * Here is where the main loop for text mode exists. Also, all the
  31. X * history is kept here and all the command parsing and execution
  32. X * and alias expansion in or out of text/graphics mode is done here.
  33. X */
  34. X
  35. X#include "mush.h"
  36. X
  37. X#ifdef BSD
  38. X#include <sys/wait.h>
  39. X#else
  40. X#ifndef SYSV
  41. X#include <wait.h>
  42. X#endif /* SYSV */
  43. X#endif /* BSD */
  44. X
  45. X#define ever (;;)
  46. X#define MAXARGS        100
  47. X#define isdelimeter(c)    (index(" \t;|", c))
  48. X
  49. Xchar *alias_expand(), *hist_expand(), *reference_hist(), *hist_from_str();
  50. Xchar **calloc();
  51. X
  52. Xstruct history {
  53. X    int histno;
  54. X    char **argv;
  55. X    struct history *prev;
  56. X    struct history *next;
  57. X};
  58. Xstatic struct history *hist_head, *hist_tail;
  59. X#define malloc(n)    (struct history *)calloc((unsigned)1,(unsigned)(n))
  60. X#define NULL_HIST    (struct history *)0
  61. X
  62. Xstatic char *last_aliased;
  63. Xstatic int hist_size, print_only;
  64. X
  65. Xdo_loop()
  66. X{
  67. X    register char *p, **argv;
  68. X    char      **last_argv = DUBL_NULL, line[256];
  69. X    int         argc, c = (iscurses - 1);
  70. X#ifdef CURSES
  71. X    int          save_echo_flg = FALSE;
  72. X#endif /* CURSES */
  73. X
  74. X    /* catch the right signals -- see main.c for other signal catching */
  75. X    (void) signal(SIGINT, catch);
  76. X    (void) signal(SIGQUIT, catch);
  77. X    (void) signal(SIGHUP, catch);
  78. X    (void) signal(SIGTERM, catch);
  79. X    (void) signal(SIGCHLD,
  80. X#ifndef SYSV
  81. X               sigchldcatcher
  82. X#else /* SYSV */
  83. X               SIG_DFL
  84. X#endif /* SYSV */
  85. X               );
  86. X
  87. X    turnoff(glob_flags, IGN_SIGS);
  88. X    if (hist_size == 0) /* if user didn't set history in .rc file */
  89. X    hist_size = 1;
  90. X
  91. X    for ever {
  92. X    if (setjmp(jmpbuf)) {
  93. X        Debug("jumped back to main loop (%s: %d)\n", __FILE__,__LINE__);
  94. X#ifdef CURSES
  95. X        if (c > 0) { /* don't pass last command back to curses_command() */
  96. X        iscurses = TRUE;
  97. X        c = hit_return();
  98. X        }
  99. X#endif /* CURSES */
  100. X    }
  101. X#ifdef CURSES
  102. X    if (iscurses || c > -1) {
  103. X        /* if !iscurses, we know that we returned from a curses-based
  104. X         * call and we really ARE still in curses. Reset tty modes!
  105. X         */
  106. X        if (ison(glob_flags, ECHO_FLAG)) {
  107. X        turnoff(glob_flags, ECHO_FLAG);
  108. X        echo_off();
  109. X        save_echo_flg = TRUE;
  110. X        }
  111. X        if (!iscurses) {
  112. X        iscurses = TRUE;
  113. X        c = hit_return();
  114. X        }
  115. X        if (c < 0)
  116. X        c = 0;
  117. X        if ((c = curses_command(c)) == -1 && save_echo_flg) {
  118. X        echo_on();
  119. X        turnon(glob_flags, ECHO_FLAG);
  120. X        save_echo_flg = FALSE;
  121. X        }
  122. X        continue;
  123. X    }
  124. X#endif /* CURSES */
  125. X    clear_msg_list(msg_list);
  126. X    (void) check_new_mail();
  127. X
  128. X    /* print a prompt according to printf like format:
  129. X     * (current message, deleted, unread, etc) are found in mail_status.
  130. X     */
  131. X    mail_status(1);
  132. X    if (Getstr(line, sizeof(line), 0) > -1)
  133. X        p = line;
  134. X    else {
  135. X        if (isatty(0) && (p = do_set(set_options, "ignoreeof"))) {
  136. X        if (!*p)
  137. X            continue;
  138. X        else
  139. X            p = strcpy(line, p); /* so processing won't destroy var */
  140. X        } else {
  141. X        putchar('\n');
  142. X        (void) quit(0, DUBL_NULL);
  143. X        continue; /* quit may return if new mail arrives */
  144. X        }
  145. X    }
  146. X
  147. X    skipspaces(0);
  148. X    if (!*p && !(p = do_set(set_options, "newline"))) {
  149. X        (void) readmsg(0, DUBL_NULL, msg_list);
  150. X        continue;
  151. X    }
  152. X    if (!*p) /* if newline is set, but no value, then continue */
  153. X        continue;
  154. X
  155. X    /* upon error, argc = -1 -- still save in history so user can
  156. X     * modify syntax error. if !argv, error is too severe.  We pass
  157. X     * the last command typed in last_argv for history reference, and
  158. X     * get back the current command _as typed_ (unexpanded by aliases
  159. X     * or history) in last_argv.
  160. X     */
  161. X    if (!(argv = make_command(p, &last_argv, &argc)))
  162. X        continue;
  163. X    /* now save the old argv in a newly created history structure */
  164. X    (void) add_history(0, last_argv); /* argc is currently ignored */
  165. X
  166. X    if (print_only) {
  167. X        print_only = 0;
  168. X        free_vec(argv);
  169. X    } else if (argc > -1)
  170. X        (void) do_command(argc, argv, msg_list);
  171. X    }
  172. X}
  173. X
  174. X/* Add a command to the history list
  175. X */
  176. X/*ARGSUSED*/
  177. Xadd_history(un_used, argv)
  178. Xchar **argv;
  179. X{
  180. X    struct history *new;
  181. X
  182. X    if (!(new = malloc(sizeof (struct history))))
  183. X    error("can't increment history");
  184. X    else {
  185. X    new->histno = ++hist_no;
  186. X    new->argv = argv;    /* this is the command _as typed_ */
  187. X    new->next = NULL_HIST;
  188. X    new->prev = hist_head;
  189. X    /* if first command, the tail of the list is "new" because
  190. X     * nothing is in the list.  If not the first command, the
  191. X     * head of the list's "next" pointer points to the new command.
  192. X     */
  193. X    if (hist_head)
  194. X        hist_head->next = new;
  195. X    else
  196. X        hist_tail = new;
  197. X    hist_head = new;
  198. X    }
  199. X    /*
  200. X     * truncate the history list to the size of the history.
  201. X     * Free the outdated command (argv) and move the tail closer to front.
  202. X     * use a while loop in case the last command reset histsize to "small"
  203. X     */
  204. X    while (hist_head->histno - hist_tail->histno >= hist_size) {
  205. X    hist_tail = hist_tail->next;
  206. X    free_vec(hist_tail->prev->argv);
  207. X    xfree(hist_tail->prev);
  208. X    hist_tail->prev = NULL_HIST;
  209. X    }
  210. X}
  211. X
  212. X/* make a command from "buf".
  213. X * first, expand history references. make an argv from that and save
  214. X * in last_argv (to be passed back and stored in history). After that,
  215. X * THEN expand aliases. return that argv to be executed as a command.
  216. X */
  217. Xchar **
  218. Xmake_command(start, last_argv, argc)
  219. Xregister char *start, ***last_argv;
  220. Xint *argc;
  221. X{
  222. X    register char *p, **tmp;
  223. X    char buf[BUFSIZ];
  224. X
  225. X    if (!last_argv)
  226. X    tmp = DUBL_NULL;
  227. X    else
  228. X    tmp = *last_argv;
  229. X    /* first expand history -- (here's where argc gets set)
  230. X     * pass the buffer, the history list to reference if \!* (or whatever)
  231. X     * result in static buffer (pointed to by p) -- even if history parsing is
  232. X     * ignored, do this to remove \'s behind !'s and verifying matching quotes
  233. X     */
  234. X    if (!(p = hist_expand(start, tmp, argc)) || Strcpy(buf, p) > sizeof buf)
  235. X    return DUBL_NULL;
  236. X    /* if history was referenced in the command, echo new command */
  237. X    if (*argc)
  238. X    puts(buf);
  239. X
  240. X    /* argc may == -1; ignore this error for now but catch it later */
  241. X    if (!(tmp = mk_argv(buf, argc, 0)))
  242. X    return DUBL_NULL;
  243. X
  244. X    /* save this as the command typed */
  245. X    if (last_argv)
  246. X    *last_argv = tmp;
  247. X
  248. X    /* expand all aliases (recursively)
  249. X     * pass _this_ command (as typed and without aliases) to let aliases
  250. X     * with "!*" be able to reference the command line just typed.
  251. X     */
  252. X    if (alias_stuff(buf, *argc, tmp) == -1)
  253. X    return DUBL_NULL;
  254. X
  255. X    if (!last_argv)
  256. X    free_vec(tmp);
  257. X
  258. X    /* with everything expanded, build final argv from new buffer
  259. X     * Note that backslashes and quotes still exist. Those are removed
  260. X     * because argument final is 1.
  261. X     */
  262. X    tmp = mk_argv(buf, argc, 1);
  263. X    return tmp;
  264. X}
  265. X
  266. X/*
  267. X * do the command specified by the argument vector, argv.
  268. X * First check to see if argc < 0. If so, someone called this
  269. X * command and they should not have! make_command() will return
  270. X * an argv but it will set argc to -1 if there's a syntax error.
  271. X */
  272. Xdo_command(argc, argv, list)
  273. Xchar **argv, list[];
  274. X{
  275. X    register char *p;
  276. X    char **tmp = argv, *next_cmd = NULL;
  277. X    int i, status;
  278. X    long do_pipe = ison(glob_flags, DO_PIPE);
  279. X
  280. X    if (argc <= 0) {
  281. X    turnoff(glob_flags, DO_PIPE);
  282. X    return -1;
  283. X    }
  284. X
  285. X    clear_msg_list(list);
  286. X
  287. X    for (i = 0; do_pipe >= 0 && argc; argc--) {
  288. X    p = argv[i];
  289. X    /* mk_argv inserts a boolean in argv[i][2] for separators */
  290. X    if ((!strcmp(p, "|") || !strcmp(p, ";")) && p[2]) {
  291. X        if (do_pipe = (*p == '|'))
  292. X        turnon(glob_flags, DO_PIPE);
  293. X        else if (next_cmd = argv[i+1])
  294. X        argv[i+1] = NULL, argc--;
  295. X        argv[i] = NULL;
  296. X        if ((status = exec_argv(i, argv, list)) <= -1)
  297. X        mac_flush();
  298. X        /* if piping, then don't call next command if this one failed. */
  299. X        if (status <= -1 && do_pipe) {
  300. X        print("Broken pipe.\n");
  301. X        do_pipe = -1, turnoff(glob_flags, DO_PIPE);
  302. X        }
  303. X        /* if command failed and piping, or command worked and not piping */
  304. X        if (do_pipe <= 0)
  305. X        status = 0, clear_msg_list(list);
  306. X        /* else command worked and piping: set is_pipe */
  307. X        else if (!status)
  308. X        turnon(glob_flags, IS_PIPE), turnoff(glob_flags, DO_PIPE);
  309. X        argv[i] = p;
  310. X        argv += (i+1);
  311. X        i = 0;
  312. X    } else
  313. X        i++;
  314. X    }
  315. X    if (*argv && do_pipe >= 0)
  316. X    if ((status = exec_argv(i, argv, list)) < 0)
  317. X        mac_flush();
  318. X    Debug("freeing: "), print_argv(tmp);
  319. X    free_vec(tmp);
  320. X    turnoff(glob_flags, DO_PIPE), turnoff(glob_flags, IS_PIPE);
  321. X    if (next_cmd) {
  322. X    if (tmp = mk_argv(next_cmd, &argc, 1))
  323. X        status = do_command(argc, tmp, list);
  324. X    else
  325. X        status = argc;
  326. X    xfree(next_cmd);
  327. X    }
  328. X    return status;
  329. X}
  330. X
  331. Xexec_argv(argc, argv, list)
  332. Xregister char **argv, list[];
  333. X{
  334. X    register int n;
  335. X
  336. X    if (!argv || !*argv || argv[0][0] == '\\' && !argv[0][1]) {
  337. X    if (ison(glob_flags, IS_PIPE))
  338. X        print("Invalid null command.\n");
  339. X    else if (ison(glob_flags, DO_PIPE)) {
  340. X        set_msg_bit(list, current_msg);
  341. X        return 0;
  342. X    }
  343. X    return -1;
  344. X    } else if (argv[0][0] == '\\') {
  345. X    /* Can't change *argv (breaks free_vec),
  346. X     *  so shift to remove the backslash
  347. X         */
  348. X    for (n = 0; argv[0][n]; n++)
  349. X        argv[0][n] = argv[0][n+1];
  350. X    }
  351. X    Debug("executing: "), print_argv(argv);
  352. X
  353. X    /* if interrupted during execution of a command, return -1 */
  354. X    if (isoff(glob_flags, IGN_SIGS) && setjmp(jmpbuf)) {
  355. X    Debug("jumped back to exec_argv (%s: %d)\n", __FILE__, __LINE__);
  356. X    return -1;
  357. X    }
  358. X
  359. X    /* standard commands */
  360. X    for (n = 0; cmds[n].command; n++)
  361. X    if (!strcmp(argv[0], cmds[n].command))
  362. X        return (*cmds[n].func)(argc, argv, list);
  363. X
  364. X    /* ucb-Mail compatible commands */
  365. X    for (n = 0; ucb_cmds[n].command; n++)
  366. X    if (!strcmp(argv[0], ucb_cmds[n].command))
  367. X        return (*ucb_cmds[n].func)(argc, argv, list);
  368. X
  369. X    /* for hidden, undocumented commands */
  370. X    for (n = 0; hidden_cmds[n].command; n++)
  371. X    if (!strcmp(argv[0], hidden_cmds[n].command))
  372. X        return (*hidden_cmds[n].func)(argc, argv, list);
  373. X
  374. X#ifdef SUNTOOL
  375. X    /* check tool-only commands */
  376. X    if (istool)
  377. X    for (n = 0; fkey_cmds[n].command; n++)
  378. X        if (!strcmp(argv[0], fkey_cmds[n].command))
  379. X        return (*fkey_cmds[n].func)(argc, argv);
  380. X#endif /* SUNTOOL */
  381. X
  382. X    n = -1; /* default to failure */
  383. X    if ((isdigit(**argv) || index("^.*$-`{}", **argv))
  384. X            && (n = get_msg_list(argv, list)) != 0) {
  385. X    if (n > 0 && isoff(glob_flags, DO_PIPE))
  386. X        for (n = 0; n < msg_cnt; n++)
  387. X        if (msg_bit(list, n)) {
  388. X            display_msg((current_msg = n), (long)0);
  389. X            unset_msg_bit(list, n);
  390. X        }
  391. X    return 0;
  392. X    } else {
  393. X    /* get_msg_list will set the current message bit if nothing parsed */
  394. X    if (n == 0)
  395. X        unset_msg_bit(list, current_msg);
  396. X    if (strlen(*argv) == 1 && index("$^.", **argv)) {
  397. X        if (!msg_cnt)
  398. X        print("No messages.");
  399. X        else {
  400. X        if (**argv != '.')
  401. X            current_msg = (**argv == '$') ? msg_cnt-1 : 0;
  402. X        set_msg_bit(list, current_msg);
  403. X        display_msg(current_msg, (long)0);
  404. X        }
  405. X        return 0;
  406. X    }
  407. X    }
  408. X
  409. X    if (!istool && do_set(set_options, "unix")) {
  410. X    if (ison(glob_flags, IS_PIPE)) {
  411. X        return pipe_msg(argc, argv, list);
  412. X    } else
  413. X        execute(argv);  /* try to execute a unix command */
  414. X    return -1; /* doesn't affect messages! */
  415. X    }
  416. X
  417. X    print("%s: command not found.\n", *argv);
  418. X    if (!istool)
  419. X    print("type '?' for valid commands, or type `help'\n");
  420. X    return -1;
  421. X}
  422. X
  423. X/* recursively look for aliases on a command line.  aliases may
  424. X * reference other aliases.
  425. X */
  426. Xalias_stuff(b, argc, Argv)
  427. Xregister char     *b, **Argv;
  428. X{
  429. X    register char     *p, **argv = DUBL_NULL;
  430. X    register int     n = 0, i = 0, Argc;
  431. X    static int         loops;
  432. X    int         dummy;
  433. X
  434. X    if (++loops == 20) {
  435. X    print("Alias loop.\n");
  436. X    return -1;
  437. X    }
  438. X    for (Argc = 0; Argc < argc; Argc++) {
  439. X    register char *h = Argv[n + ++i];
  440. X    register char *p2 = "";
  441. X    int sep;
  442. X
  443. X    /* we've hit a command separator or the end of the line */
  444. X    if (h && strcmp(h, ";") && strcmp(h, "|"))
  445. X        continue;
  446. X
  447. X    /* create a new argv containing this (possible subset) of argv */
  448. X    if (!(argv = calloc((unsigned)(i+1), sizeof (char *))))
  449. X        continue;
  450. X    sep = n + i;
  451. X    while (i--)
  452. X        strdup(argv[i], Argv[n+i]);
  453. X
  454. X    if ((!last_aliased || strcmp(last_aliased, argv[0]))
  455. X            && (p = alias_expand(argv[0]))) {
  456. X        /* if history was referenced, ignore the rest of argv
  457. X         * else copy all of argv onto the end of the buffer.
  458. X         */
  459. X        if (!(p2 = hist_expand(p, argv, &dummy)))
  460. X        break;
  461. X        if (!dummy)
  462. X        (void) argv_to_string(p2+strlen(p2), argv+1);
  463. X        if (Strcpy(b, p2) > BUFSIZ) {
  464. X        print("Not enough buffer space.\n");
  465. X        break;
  466. X        }
  467. X        /* release old argv and build a new one based on new string */
  468. X        free_vec(argv);
  469. X        if (!(argv = mk_argv(b, &dummy, 0)))
  470. X        break;
  471. X        if (alias_stuff(b, dummy, argv) == -1)
  472. X        break;
  473. X    } else
  474. X        b = argv_to_string(b, argv);
  475. X    xfree(last_aliased), last_aliased = NULL;
  476. X    free_vec(argv);
  477. X    b += strlen(b);
  478. X    if (h) {
  479. X        b += strlen(sprintf(b, " %s ", h));
  480. X        while (++Argc < argc && (h = Argv[Argc]))
  481. X        if (Argc > sep && strcmp(h, ";"))
  482. X            break;
  483. X        n = Argc--;
  484. X    }
  485. X    i = 0;
  486. X    }
  487. X    xfree(last_aliased), last_aliased = NULL;
  488. X    --loops;
  489. X    if (Argc < argc) {
  490. X    free_vec(argv);
  491. X    return -1;
  492. X    }
  493. X    return 0;
  494. X}
  495. X
  496. Xchar *
  497. Xalias_expand(cmd)
  498. Xregister char *cmd;
  499. X{
  500. X    register char *p;
  501. X    register int x;
  502. X
  503. X    if (!(p = do_set(functions, cmd)))
  504. X    return NULL;
  505. X    last_aliased = savestr(cmd); /* to be freed elsewhere; don't strdup! */
  506. X    if (isoff(glob_flags, WARNING))
  507. X    return p;
  508. X    for (x = 0; cmds[x].command; x++)
  509. X    if (!strcmp(cmd, cmds[x].command)) {
  510. X        wprint("(real command: \"%s\" aliased to \"%s\")\n", cmd, p);
  511. X        return p;
  512. X    }
  513. X    for (x = 0; ucb_cmds[x].command; x++)
  514. X    if (!strcmp(cmd, ucb_cmds[x].command)) {
  515. X        wprint("(ucb-command: \"%s\" aliased to \"%s\")\n", cmd, p);
  516. X        return p;
  517. X    }
  518. X    return p;
  519. X}
  520. X
  521. Xstatic int nonobang;
  522. X
  523. X/* expand history references and separate message lists from other tokens */
  524. Xchar *
  525. Xhist_expand(str, argv, hist_was_referenced)
  526. Xregister char *str, **argv;
  527. Xregister int *hist_was_referenced;
  528. X{
  529. X    static char   buf[BUFSIZ];
  530. X    register int  b = 0, inquotes = 0;
  531. X    int       first_space = 0, ignore_bang;
  532. X
  533. X    ignore_bang = (ison(glob_flags, IGN_BANG) ||
  534. X           do_set(set_options, "ignore_bang"));
  535. X    nonobang = !!do_set(set_options, "nonobang");
  536. X
  537. X    if (hist_was_referenced)
  538. X    *hist_was_referenced = 0;
  539. X    while (*str) {
  540. X    while (!inquotes && isspace(*str))
  541. X        str++;
  542. X    do  {
  543. X        if (!*str)
  544. X        break;
  545. X        if (b >= sizeof(buf)-1) {
  546. X        print("argument list too long.\n");
  547. X        return NULL;
  548. X        }
  549. X        if ((buf[b] = *str++) == '\'') {
  550. X        /* make sure there's a match! */
  551. X        inquotes = !inquotes;
  552. X        }
  553. X        if (!first_space && !inquotes && index("0123456789{}*$^.", buf[b])
  554. X                 && b && !index("0123456789{}-^. \t", buf[b-1])) {
  555. X        buf[b+1] = buf[b];
  556. X        buf[b++] = ' ';
  557. X        while ((buf[++b] = *str++) && index("0123456789-,${}.", buf[b]))
  558. X            ;
  559. X        if (!buf[b])
  560. X            str--;
  561. X        first_space++;
  562. X        }
  563. X        /* check for (;) (|) or any other delimiter and separate it from
  564. X         * other tokens.
  565. X         */
  566. X        if (!inquotes && buf[b] != '\0' && isdelimeter(buf[b]) &&
  567. X            (b < 0 || buf[b-1] != '\\')) {
  568. X        if (!isspace(buf[b]))
  569. X            first_space = -1; /* resume msg-list separation */
  570. X        if (b && !isspace(buf[b-1]))
  571. X            buf[b+1] = buf[b], buf[b++] = ' ';
  572. X        b++;
  573. X        break;
  574. X        }
  575. X        /*
  576. X         * If double-quotes, just copy byte by byte, char by char,
  577. X         *  but do remove backslashes from in front of !s
  578. X         */
  579. X        if (buf[b] == '"') {
  580. X        int B = b;
  581. X        while ((buf[++B] = *str++) && buf[B] != '"')
  582. X            if (*str == '!' && buf[B] == '\\')
  583. X            buf[B] = '!', str++;
  584. X        if (buf[B])
  585. X            b = B;
  586. X        else
  587. X            str--;
  588. X        b++;
  589. X        continue;
  590. X        }
  591. X        if (buf[b] == '\\') {
  592. X        if ((buf[++b] = *str) == '!')
  593. X            buf[--b] = '!';
  594. X        ++str;
  595. X        } else if (buf[b] == '!' && *str && *str != '\\' && !isspace(*str)
  596. X               && !ignore_bang) {
  597. X        char word[BUFSIZ], *s;
  598. X        if (!(s = reference_hist(str, word, argv))) {
  599. X            if (!nonobang)
  600. X            return NULL;
  601. X        } else {
  602. X            str = s;
  603. X            if (hist_was_referenced)
  604. X            *hist_was_referenced = 1;
  605. X            if (strlen(word) + b >= sizeof buf) {
  606. X            print("argument list too long.\n");
  607. X            return NULL;
  608. X            }
  609. X            b += Strcpy(&buf[b], word) - 1;
  610. X        }
  611. X        }
  612. X        b++;
  613. X    } while (*str && (!isdelimeter(*str) || str[-1] == '\\'));
  614. X    if (!inquotes)
  615. X        first_space++, buf[b++] = ' ';
  616. X    }
  617. X    buf[b] = 0;
  618. X    return buf;
  619. X}
  620. X
  621. X/*
  622. X * expand references to internal variables.  This allows such things
  623. X * as $iscurses, $hdrs_only, etc. to work correctly.
  624. X */
  625. Xchar *
  626. Xcheck_internal(str)
  627. Xregister char *str;
  628. X{
  629. X    int ret_val = -1;
  630. X
  631. X    if (!strcmp(str, "iscurses"))
  632. X    ret_val = (iscurses || ison(glob_flags, PRE_CURSES));
  633. X    else if (!strcmp(str, "istool"))
  634. X    ret_val = istool;
  635. X    else if (!strcmp(str, "hdrs_only"))
  636. X    return hdrs_only;
  637. X    else if (!strcmp(str, "is_sending"))
  638. X    ret_val = (ison(glob_flags, IS_SENDING) != 0);
  639. X    else if (!strcmp(str, "redirect"))
  640. X    ret_val = (ison(glob_flags, REDIRECT) != 0);
  641. X    else if (!strcmp(str, "thisfolder"))
  642. X    return (mailfile && *mailfile) ? mailfile : NULL;
  643. X
  644. X    return ret_val > 0 ? "1" : ret_val == 0? "0" : NULL;
  645. X}
  646. X
  647. X/*
  648. X * find mush variable references and expand them to their values.
  649. X * variables are preceded by a '$' and cannot be within single
  650. X * quotes.  Only if expansion has been made do we copy buf back into str.
  651. X * We expand only as far as the first unprotected `;' separator in str,
  652. X * to get the right behavior when multiple commands are on one line.
  653. X * RETURN 0 on failure, 1 on success.
  654. X */
  655. Xvariable_expand(str)
  656. Xregister char *str;
  657. X{
  658. X    register int     b = 0, inquotes = 0;
  659. X    char             buf[BUFSIZ], *start = str;
  660. X    int             expanded = 0;
  661. X
  662. X    while (*str && b < sizeof buf - 1) {
  663. X    if (*str == '~' && (str == start || isspace(*(str-1)))) {
  664. X        register char *p = any(str, " \t"), *tmp;
  665. X        int x = 1;
  666. X        if (p)
  667. X        *p = 0;
  668. X        tmp = getpath(str, &x);
  669. X        /* if error, print message and return 0 */
  670. X        if (x == -1) {
  671. X        wprint("%s: %s\n", str, tmp);
  672. X        return 0;
  673. X        }
  674. X        b += Strcpy(buf+b, tmp);
  675. X        if (p)
  676. X        *p = ' ', str = p;
  677. X        else
  678. X        str += strlen(str);
  679. X        expanded = 1;
  680. X    }
  681. X    /* if single-quotes, just copy byte by byte, char by char ... */
  682. X    if ((buf[b] = *str++) == '\'' && !inquotes) {
  683. X        while ((buf[++b] = *str++) && buf[b] != '\'')
  684. X        ;
  685. X        if (!buf[b])
  686. X        str--;
  687. X    } else if (!inquotes && buf[b] == '\\' && *str) {
  688. X        buf[++b] = *str++;
  689. X        b++;
  690. X        continue;
  691. X    } else if (buf[b] == '"')
  692. X        inquotes = !inquotes;
  693. X    /* If $ is eol, continue.  Variables must start with a `$'
  694. X     * and continue with {, _, a-z, A-Z or it is not a variable.      }
  695. X     */
  696. X    if (buf[b] == '$' && *str &&
  697. X        (isalpha(*str) || *str == '{'/*}*/ ||
  698. X            *str == '_' || *str == '?')) {
  699. X        register char c, *p, *var, *end, do_bool, *op = NULL;
  700. X
  701. X        if (do_bool = (*str == '?'))
  702. X        str++;
  703. X        if (*(end = var = str) == '{')  /* } */   {
  704. X        if (*++var == '?' && !do_bool)
  705. X            ++var, do_bool = 1;
  706. X        if (!isalpha(*var) && *var != '_') {
  707. X            print("Illegal variable name.\n");
  708. X            return 0;
  709. X        }
  710. X        if (!(end = index(var, '}'))) /* { */   {
  711. X            print("Unmatched '{'.\n"); /* } */
  712. X            return 0;
  713. X        }
  714. X        *end++ = 0;
  715. X        } else {
  716. X        while (isalnum(*++end) || *end == '_')
  717. X            ;
  718. X        if (!do_bool && end[0] == ':' && isalpha(end[1]))
  719. X            end += 2; /* include colon and operation */
  720. X        }
  721. X        /* advance "str" to the next parse-point, replace the end
  722. X         * of "var" (end) with a nul, and save char in `c'
  723. X         */
  724. X        c = *(str = end), *end = 0;
  725. X
  726. X        if (!do_bool && (op = index(var, ':')) && op[1])
  727. X        *op++ = '\0'; /* replace colon with nul */
  728. X
  729. X        /* get the value of the variable. */
  730. X        if (!(p = check_internal(var)))
  731. X        p = do_set(set_options, var);
  732. X        if (do_bool)
  733. X        buf[b++] = p ? '1' : '0';
  734. X        else if (p) {
  735. X        if (op) {
  736. X            char *cut = rindex(p, '/');
  737. X            if (cut)
  738. X            *cut = '\0';
  739. X            switch (*op) {
  740. X            case 't':
  741. X                if (cut)
  742. X                p = cut + 1;
  743. X                /* Fall through! */
  744. X            case 'h':
  745. X                b += Strcpy(buf+b, p);
  746. X            otherwise:
  747. X                *str = c;
  748. X                *--op = ':';
  749. X                print("Unknown colon modifier.\n");
  750. X                return 0;
  751. X            }
  752. X            if (cut)
  753. X            *cut = '/';
  754. X        } else
  755. X            b += Strcpy(buf+b, p);
  756. X        } else {
  757. X        print("%s: undefined variable\n", var);
  758. X        return 0;
  759. X        }
  760. X        expanded = 1;
  761. X        *str = c; /* replace the null with the old character */
  762. X        if (op)
  763. X        *--op = ':'; /* Put back the colon */
  764. X    } else if (!inquotes && buf[b] == ';') {
  765. X        while (buf[++b] = *str++)
  766. X        ;
  767. X        b++;
  768. X        break;
  769. X    } else
  770. X        b++;
  771. X    }
  772. X    buf[b] = 0;
  773. X    if (expanded) /* if any expansions were done, copy back into orig buf */
  774. X    (void) strcpy(start, buf);
  775. X    return 1;
  776. X}
  777. X
  778. X/* make an argv of space delimited character strings out of string "str".
  779. X * place in "argc" the number of args made.  If final is true, then expand
  780. X * variables and file names and remove quotes and backslants according to
  781. X * standard.
  782. X */
  783. Xchar **
  784. Xmk_argv(str, argc, final)
  785. Xregister char *str;
  786. Xint *argc;
  787. X{
  788. X    register char    *s = NULL, *p;
  789. X    register int    tmp, err = 0, unq_sep = 0;
  790. X    char        *newargv[MAXARGS], **argv, *p2, c, buf[BUFSIZ];
  791. X
  792. X    if (debug > 3)
  793. X    printf("Working on: %s\n",str);
  794. X    /* If final is true, do variable expansions first */
  795. X    if (final) {
  796. X    (void) strcpy(buf, str);
  797. X    str = buf;
  798. X    if (!variable_expand(str))
  799. X        return DUBL_NULL;
  800. X    }
  801. X    *argc = 0;
  802. X    while (*str && *argc < MAXARGS) {
  803. X    while (isspace(*str))
  804. X        ++str;
  805. X    /* When we have hit an unquoted `;', final must be true,
  806. X     * so we're finished.  Stuff the rest of the string at the
  807. X     * end of the argv -- do_command will pass it back later,
  808. X     * for further processing -- and break out of the loop.
  809. X     * NOTE: *s is not yet valid the first time through this
  810. X     * loop, so unq_sep should always be initialized to 0.
  811. X     */
  812. X    if (unq_sep && s && *s == ';') {
  813. X        if (*str) { /* Don't bother saving a null string */
  814. X        newargv[*argc] = savestr(str);
  815. X        (*argc)++;
  816. X        }
  817. X        break;
  818. X    }
  819. X    if (*str) {        /* found beginning of a word */
  820. X        unq_sep = 0;    /* innocent until proven guilty */
  821. X        s = p = str;
  822. X        do  {
  823. X        if (p - s >= sizeof buf-1) {
  824. X            print("argument list too long.\n");
  825. X            return DUBL_NULL;
  826. X        }
  827. X        if (*str == ';' || *str == '|')
  828. X            unq_sep = final; /* Mark an unquoted separator */
  829. X        if ((*p = *str++) == '\\') {
  830. X            if (final && (*str == ';' || *str == '|'))
  831. X            --p; /* Back up to overwrite the backslash */
  832. X            if (*++p = *str) /* assign and compare to NUL */
  833. X            str++;
  834. X            continue;
  835. X        }
  836. X        if (p2 = index("\"'", *p)) {
  837. X            register char c2 = *p2;
  838. X            /* you can't escape quotes inside quotes of the same type */
  839. X            if (!(p2 = index(str, c2))) {
  840. X            if (final)
  841. X                print("Unmatched %c.\n", c2);
  842. X            err++;
  843. X            p2 = str;
  844. X            }
  845. X            tmp = (int)(p2 - str) + 1; /* take up to & include quote */
  846. X            (void) strncpy(p + !final, str, tmp);
  847. X            p += tmp - 2 * !!final; /* change final to a boolean */
  848. X            if (*(str = p2))
  849. X            str++;
  850. X        }
  851. X        } while (++p, *str && (!isdelimeter(*str) || str[-1] == '\\'));
  852. X        if (c = *str) /* set c = *str, check for null */
  853. X        str++;
  854. X        *p = 0;
  855. X        if (*s) {
  856. X        /* To differentiate real separators from quoted or
  857. X         * escaped ones, always store 3 chars:
  858. X         *  1) The separator character
  859. X         *  2) A nul (string terminator)
  860. X         *  3) An additional boolean (0 or 1)
  861. X         * The boolean is checked by do_command.  Note that this
  862. X         * applies only to "solitary" separators, i.e. those not
  863. X         * part of a larger word.
  864. X         */
  865. X        if (final && (!strcmp(s, ";") || !strcmp(s, "|"))) {
  866. X            char *sep = savestr("xx"); /* get 3 char slots */
  867. X            sep[0] = *s, sep[1] = '\0', sep[2] = unq_sep;
  868. X            newargv[*argc] = sep;
  869. X        } else
  870. X            newargv[*argc] = savestr(s);
  871. X        (*argc)++;
  872. X        }
  873. X        *p = c;
  874. X    }
  875. X    }
  876. X    if (!*argc)
  877. X    return DUBL_NULL;
  878. X    /* newargv[*argc] = NULL; */
  879. X    if (!(argv = calloc((unsigned)((*argc)+1), sizeof(char *)))) {
  880. X    perror("mk_argv: calloc");
  881. X    return DUBL_NULL;
  882. X    }
  883. X    for (tmp = 0; tmp < *argc; tmp++)
  884. X    argv[tmp] = newargv[tmp];
  885. X    if (err)
  886. X    *argc = -1;
  887. X    if (debug > 3)
  888. X    printf("Made argv: "), print_argv(argv);
  889. X    return argv;
  890. X}
  891. X
  892. X/*
  893. X * Report a history parsing error.
  894. X * Suppress the message if nonobang is true.
  895. X */
  896. X#define hist_error    if (nonobang) {;} else print
  897. X
  898. X/*
  899. X * reference previous history from syntax of str and place result into buf
  900. X * We know we've got a history reference -- we're passed the string starting
  901. X * the first char AFTER the '!' (which indicates history reference)
  902. X */
  903. Xchar *
  904. Xreference_hist(str, buf, hist_ref)
  905. Xregister char *str, **hist_ref;
  906. Xchar buf[];
  907. X{
  908. X    int        relative; /* relative from current hist_no */
  909. X    int        old_hist, argstart = 0, lastarg, argend = 0, n = 0;
  910. X    register char  *p, *rb = NULL, **argv = hist_ref;
  911. X    struct history *hist;
  912. X
  913. X    buf[0] = 0;
  914. X    if (*str == '{')
  915. X    if (!(rb = index(str, '}'))) {   /* { */
  916. X        hist_error("Unmatched '}'");
  917. X        return NULL;
  918. X    } else
  919. X        *rb = 0, ++str;
  920. X    relative = *str == '-';
  921. X    if (index("!:$*", *str)) {
  922. X    old_hist = hist_no;
  923. X    if (*str == '!')
  924. X        str++;
  925. X    } else if (isdigit(*(str + relative)))
  926. X    str = my_atoi(str + relative, &old_hist);
  927. X    else if (!(p = hist_from_str(str, &old_hist))) {
  928. X    if (rb) /* { */
  929. X        *rb = '}';
  930. X    return NULL;
  931. X    } else
  932. X    str = p;
  933. X    if (relative)
  934. X    old_hist = (hist_no-old_hist) + 1;
  935. X    if (old_hist == hist_no) {
  936. X    if (!(argv = hist_ref))
  937. X        hist_error("You haven't done anything yet!\n");
  938. X    } else {
  939. X    if (old_hist <= hist_no-hist_size || old_hist > hist_no ||
  940. X        old_hist <= 0) {
  941. X        if (old_hist <= 0)
  942. X        hist_error("You haven't done that many commands, yet.\n");
  943. X        else
  944. X        hist_error("Event %d %s.\n", old_hist,
  945. X            (old_hist > hist_no)? "hasn't happened yet": "expired");
  946. X        if (rb) /* { */
  947. X        *rb = '}';
  948. X        return NULL;
  949. X    }
  950. X    hist = hist_head;
  951. X    while (hist && hist->histno != old_hist)
  952. X        hist = hist->prev;
  953. X    if (hist)
  954. X        argv = hist->argv;
  955. X    }
  956. X    if (!argv) {
  957. X    if (rb) /* { */
  958. X        *rb = '}';
  959. X    return NULL;
  960. X    }
  961. X    while (argv[argend+1])
  962. X    argend++;
  963. X    lastarg = argend;
  964. X    if (*str && index(":$*-", *str)) {
  965. X    int isrange;
  966. X    if (*str == ':' && isdigit(*++str))
  967. X        str = my_atoi(str, &argstart);
  968. X    if (isrange = (*str == '-'))
  969. X        str++;
  970. X    if (!isdigit(*str)) {
  971. X        if (*str == 'p')
  972. X        str++, print_only = 1;
  973. X        else if (!*str || isdelimeter(*str))
  974. X        if (isrange)
  975. X            argend--; /* unspecified end of range implies last-1 arg */
  976. X        else
  977. X            argend = argstart; /* no range specified; use arg given */
  978. X        else {
  979. X        if (*str == '*')
  980. X            if (argv[0])
  981. X            argstart = 1, argend = ++lastarg;
  982. X            else
  983. X            argstart = 0;
  984. X        else if (*str == '$' && !isrange)
  985. X            argstart = argend;
  986. X        else if (*str != '$')
  987. X            print("%c: unknown argument selector.\n", *str);
  988. X        str++;
  989. X        }
  990. X    } else
  991. X        str = my_atoi(str, &argend);
  992. X    }
  993. X    if (argstart > lastarg || argend > lastarg || argstart > argend) {
  994. X    hist_error("Bad argument selector.\n");
  995. X    if (rb) /* { */
  996. X        *rb = '}';
  997. X    return NULL;
  998. X    }
  999. X    while (argstart <= argend) {
  1000. X    n += Strcpy(&buf[n], argv[argstart++]);
  1001. X    buf[n++] = ' ';
  1002. X    }
  1003. X    buf[--n] = 0;
  1004. X    if (rb) /* { */
  1005. X    *rb = '}';
  1006. X    return (rb ? rb + 1 : str);
  1007. X}
  1008. X
  1009. X/* find a history command that contains the string "str"
  1010. X * place that history number in "hist" and return the end of the string
  1011. X * parsed: !?foo (find command with "foo" in it) !?foo?bar (same, but add "bar")
  1012. X * in the second example, return the pointer to "bar"
  1013. X */
  1014. Xchar *
  1015. Xhist_from_str(str, hist_number)
  1016. Xregister char *str;
  1017. Xregister int *hist_number;
  1018. X{
  1019. X    register char *p = NULL, c = 0;
  1020. X    int       full_search = 0, len, found;
  1021. X    char       buf[BUFSIZ];
  1022. X    struct history *hist;
  1023. X#ifndef REGCMP
  1024. X    extern char   *re_comp();
  1025. X#else
  1026. X    extern char   *regcmp();
  1027. X#endif /* REGCMP */
  1028. X
  1029. X    /* For !{something}, the {} are stripped in reference_hist() */
  1030. X    if (*str == '?') {
  1031. X    if (p = index(++str, '?'))
  1032. X        c = *p, *p = 0;
  1033. X    else
  1034. X        p = str + strlen(str);
  1035. X    full_search = 1;
  1036. X    } else {
  1037. X    p = str;
  1038. X    while (*p && *p != ':' && !isspace(*p))
  1039. X        p++;
  1040. X    c = *p, *p = 0;
  1041. X    }
  1042. X    if (*str) {
  1043. X#ifndef REGCMP
  1044. X    if (re_comp(str))
  1045. X#else
  1046. X    if (!regcmp(str, NULL))
  1047. X#endif /* REGCMP */
  1048. X    {
  1049. X        if (c)
  1050. X        *p = c;
  1051. X        return NULL;
  1052. X    }
  1053. X    } else {
  1054. X    *hist_number = hist_no;
  1055. X    if (c)
  1056. X        *p = c;
  1057. X    return (*p == '?' ? p + 1 : p);
  1058. X    }
  1059. X    len = strlen(str);
  1060. X    /* move thru the history in reverse searching for a string match. */
  1061. X    for (hist = hist_head; hist; hist = hist->prev) {
  1062. X    if (full_search) {
  1063. X        (void) argv_to_string(buf, hist->argv);
  1064. X        Debug("Checking for (%s) in (#%d: %s)\n", str, hist->histno, buf);
  1065. X    }
  1066. X    if (!full_search) {
  1067. X        (void) strcpy(buf, hist->argv[0]);
  1068. X        Debug("Checking for (%s) in (#%d: %*s)\n",
  1069. X        str, hist->histno, len, buf);
  1070. X        found = !strncmp(buf, str, len);
  1071. X    } else
  1072. X        found =
  1073. X#ifndef REGCMP
  1074. X        re_exec(buf)
  1075. X#else
  1076. X        !!regex(str, buf, NULL) /* convert to boolean value */
  1077. X#endif /* REGCMP */
  1078. X                == 1;
  1079. X    if (found) {
  1080. X        *hist_number = hist->histno;
  1081. X        Debug("Found it in history #%d\n", *hist_number);
  1082. X        *p = c;
  1083. X        return (*p == '?' ? p + 1 : p);
  1084. X    }
  1085. X    }
  1086. X    hist_error("%s: event not found\n", str);
  1087. X    *p = c;
  1088. X    return NULL;
  1089. X}
  1090. X
  1091. Xdisp_hist(n, argv)  /* argc not used -- use space for the variable, "n" */
  1092. Xregister int n;
  1093. Xchar **argv;
  1094. X{
  1095. X    register int    list_num = TRUE, num_of_hists = hist_size;
  1096. X    register int    reverse = FALSE;
  1097. X    struct history    *hist = hist_tail;
  1098. X
  1099. X    if (!hist) {
  1100. X    print("No history yet.\n");
  1101. X    return -1;
  1102. X    }
  1103. X
  1104. X    while (*++argv && *argv[0] == '-') {
  1105. X    n = 1;
  1106. X    do  switch(argv[0][n]) {
  1107. X        case 'h': list_num = FALSE;
  1108. X        when 'r': reverse = TRUE; hist = hist_head;
  1109. X        otherwise: print("usage: history [-h] [-r] [#histories]\n");
  1110. X               return -1;
  1111. X        }
  1112. X    while (argv[0][++n]);
  1113. X    }
  1114. X    if (*argv)
  1115. X    if (!isdigit(**argv)) {
  1116. X        print("history: badly formed number\n");
  1117. X        return -1;
  1118. X    } else
  1119. X        num_of_hists = atoi(*argv);
  1120. X
  1121. X    if (num_of_hists > hist_size || num_of_hists > hist_no)
  1122. X    num_of_hists = min(hist_size, hist_no);
  1123. X
  1124. X    if (!reverse)
  1125. X    while (hist_no - hist->histno > num_of_hists) {
  1126. X        printf("skipping %d\n", hist->histno);
  1127. X        hist = hist->next;
  1128. X    }
  1129. X
  1130. X    do_pager(NULL, TRUE);
  1131. X    for (n = 0; n < num_of_hists && hist; n++) {
  1132. X    char buf[256];
  1133. X    if (list_num)
  1134. X        do_pager(sprintf(buf, "%4.d  ", hist->histno), FALSE);
  1135. X    (void) argv_to_string(buf, hist->argv);
  1136. X    (void) do_pager(buf, FALSE);
  1137. X    if (do_pager("\n", FALSE) == -1)
  1138. X        break;
  1139. X    hist = (reverse)? hist->prev : hist->next;
  1140. X    }
  1141. X    do_pager(NULL, FALSE);
  1142. X    return 0;
  1143. X}
  1144. X
  1145. Xinit_history(newsize)
  1146. X{
  1147. X    if ((hist_size = newsize) < 1)
  1148. X    hist_size = 1;
  1149. X}
  1150. END_OF_FILE
  1151. if test 30008 -ne `wc -c <'loop.c'`; then
  1152.     echo shar: \"'loop.c'\" unpacked with wrong size!
  1153. fi
  1154. # end of 'loop.c'
  1155. fi
  1156. echo shar: End of archive 14 \(of 19\).
  1157. cp /dev/null ark14isdone
  1158. MISSING=""
  1159. for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ; do
  1160.     if test ! -f ark${I}isdone ; then
  1161.     MISSING="${MISSING} ${I}"
  1162.     fi
  1163. done
  1164. if test "${MISSING}" = "" ; then
  1165.     echo You have unpacked all 19 archives.
  1166.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  1167. else
  1168.     echo You still need to unpack the following archives:
  1169.     echo "        " ${MISSING}
  1170. fi
  1171. ##  End of shell archive.
  1172. exit 0
  1173.